package zack.project.domain.trade.service.settlement;

import zack.project.domain.trade.adapter.repository.ITradeRepository;
import zack.project.domain.trade.model.aggregate.GroupBuyTeamSettlementAggregate;
import zack.project.domain.trade.model.entity.*;
import zack.project.domain.trade.service.ITradeSettlementOrderService;
import zack.project.domain.trade.service.ITradeTaskService;
import zack.project.domain.trade.service.settlement.factory.TradeSettlementRuleFilterFactory;
import zack.project.types.exception.AppException;
import cn.bugstack.wrench.design.framework.link.model2.chain.BusinessLinkedList;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @description 拼团交易结算服务
 * @create 2025-01-26 15:22
 */
@Slf4j
@Service
public class TradeSettlementOrderService implements ITradeSettlementOrderService {

    @Resource
    private ITradeRepository repository;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;
    @Resource
    private ITradeTaskService tradeTaskService;

    @Resource
    private BusinessLinkedList<TradeSettlementRuleCommandEntity,
            TradeSettlementRuleFilterFactory.DynamicContext,
            TradeSettlementRuleFilterBackEntity> tradeSettlementRuleFilter;

    /**
     * @param tradePaySuccessEntity 交易支付订单实体对象，
     *                              这个支付成功的实体类的outTradeNo字段在{group_buy_order_list}表中，是请求锁单dto实体对象外部传入的
     *                              并传递给外部服务在对该笔拼团详单进行支付后产生的TradePaySuccessEntity的outTradeNo
     * @return
     * @throws Exception
     */
    @Override
    public TradePaySettlementEntity settlementMarketPayOrder(TradePaySuccessEntity tradePaySuccessEntity) throws Exception {
        log.info("拼团交易-支付订单结算:{} outTradeNo:{}", tradePaySuccessEntity.getUserId(), tradePaySuccessEntity.getOutTradeNo());
        // 1. 结算规则过滤
        TradeSettlementRuleFilterBackEntity tradeSettlementRuleFilterBackEntity =
                tradeSettlementRuleFilter.apply(
                        //构建交易结算指令实体:
                        //sc：结算商品的来源渠道，scRuleFilter根据dccService里面的blackList属性，如果当前的来源和渠道在黑名单中则取消结算
                        //userId，outTradeNo：OutTradeNoRuleFilter节点先根据用户id和外部单号id查{group_buy_order_list}找到对应的锁单记录并设置到动态上下文中(marketProductEntity)
                        //如果该记录是CLOSE关闭状态(被退单了)，则终止结算.
                        //outTradeTime：settableRuleFilter节点根据上下文的marketProduct获取teamId，根据该队伍id去查{group_buy_order}拼团记录(GroupBuyTeamEntity)，
                        //比较拼团的有效时间和支付完成的时间。
                        TradeSettlementRuleCommandEntity.builder()
                                .source(tradePaySuccessEntity.getSource())
                                .channel(tradePaySuccessEntity.getChannel())
                                .userId(tradePaySuccessEntity.getUserId())
                                .outTradeNo(tradePaySuccessEntity.getOutTradeNo())
                                .outTradeTime(tradePaySuccessEntity.getOutTradeTime())
                                .build(),
                        new TradeSettlementRuleFilterFactory.DynamicContext());

        String teamId = tradeSettlementRuleFilterBackEntity.getTeamId();

        // 2. 查询组团信息
        GroupBuyTeamEntity groupBuyTeamEntity = GroupBuyTeamEntity.builder()
                .teamId(tradeSettlementRuleFilterBackEntity.getTeamId())
                .activityId(tradeSettlementRuleFilterBackEntity.getActivityId())
                .targetCount(tradeSettlementRuleFilterBackEntity.getTargetCount())
                .completeCount(tradeSettlementRuleFilterBackEntity.getCompleteCount())
                .lockCount(tradeSettlementRuleFilterBackEntity.getLockCount())
                .status(tradeSettlementRuleFilterBackEntity.getStatus())
                .validStartTime(tradeSettlementRuleFilterBackEntity.getValidStartTime())
                .validEndTime(tradeSettlementRuleFilterBackEntity.getValidEndTime())
                .notifyConfigVO(tradeSettlementRuleFilterBackEntity.getNotifyConfigVO())
                .build();

        // 3. 构建聚合对象，用拼团队伍实体来更改拼团单的complete_count，支付成功实体更改对应的拼团详单的状态
        GroupBuyTeamSettlementAggregate groupBuyTeamSettlementAggregate =
                GroupBuyTeamSettlementAggregate.builder()
                        .userEntity(UserEntity.builder().userId(tradePaySuccessEntity.getUserId()).build())
                        .groupBuyTeamEntity(groupBuyTeamEntity)
                        .tradePaySuccessEntity(tradePaySuccessEntity)
                        .build();

        // 4. 拼团交易结算，进行结算的时候会判断当前拼团单的完成数量和目标数量是否相等，
        // 如果相等说明拼团完成，生成回调通知该笔拼团完成
        List<NotifyTaskEntity> notifyTaskEntityList = repository.settlementMarketPayOrder(groupBuyTeamSettlementAggregate);

        // 5. 组队回调处理 - 处理失败也会有定时任务补偿，通过这样的方式，可以减轻任务调度，提高时效性
        if (null != notifyTaskEntityList) {
            threadPoolExecutor.execute(() -> {
                Map<String, Integer> notifyResultMap = null;
                try {
                    notifyResultMap = tradeTaskService.execNotifyJob(notifyTaskEntityList);
                    log.info("回调通知拼团完结 result:{}", JSON.toJSONString(notifyResultMap));
                } catch (Exception e) {
                    log.error("回调通知拼团完结失败 result:{}", JSON.toJSONString(notifyResultMap), e);
                    throw new AppException(e.getMessage());
                }
            });
        }

        // 6. 返回结算信息 - 公司中开发这样的流程时候，会根据外部需要进行值的设置
        return TradePaySettlementEntity.builder()
                .source(tradePaySuccessEntity.getSource())
                .channel(tradePaySuccessEntity.getChannel())
                .userId(tradePaySuccessEntity.getUserId())
                .teamId(teamId)
                .activityId(groupBuyTeamEntity.getActivityId())
                .outTradeNo(tradePaySuccessEntity.getOutTradeNo())
                .build();
    }

}
